<script lang="ts" setup>
import mapboxgl from 'mapbox-gl'
import { ElTree } from 'element-plus'
import { LayerInfoService } from '@supermapgis/iclient-mapboxgl'
import { LAYER_TREE_DATA, LAYER_BOUNDS, SOURCENODE_IDS, URL_CONFIGS } from '@/constants'
import { useL7Layer } from '@/hooks'
import http from '@/utils/http'

interface Tree {
  pid?: number
  id: number
  uid: string
  label: string
  children?: Tree[]
}

interface MapboxLayerOptions {
  id: string
  type: string
  source: string
  filter: (string | string[])[]
  layout?: { [key: string]: string | number | string[] }
  paint?: { [key: string]: (number | string | string[])[] | string | number }
}

type BoundsKey = keyof typeof LAYER_BOUNDS

const treeRef = ref<InstanceType<typeof ElTree>>()
const defaultProps = {
  children: 'children',
  label: 'label'
}
const layersData = ref<Tree[]>(LAYER_TREE_DATA)
let lastClickTime = 0
let lastClickedNode: any = null

onMounted(async () => {
  try {
    const layersInfo = await new LayerInfoService(URL_CONFIGS.DY_MAP_URL).getLayersInfo()
    const layers = layersInfo.result.subLayers.layers
    const layerTree = layers.map((item: any, index: number) => {
      const baseLayer = {
        pid: 0,
        id: index,
        uid: `0:${index}`,
        label: item.caption
      }
      if (item.subLayers?.layers?.length) {
        const children: Tree[] = item.subLayers.layers.map((subItem: any, subIndex: number) => ({
          pid: index,
          id: subIndex,
          uid: `${index}.${subIndex}`,
          label: subItem.caption
        }))
        return { ...baseLayer, children }
      } else {
        return baseLayer
      }
    })
    layersData.value[0].children = layerTree
    nextTick(() => {
      const treeUids = getAllUids(layersData.value)
      // treeUids.pop()
      treeUids.splice(-5)
      treeRef.value!.setCheckedKeys(treeUids)
    })
  } catch (error) {
    throw new Error(error instanceof Error ? error.message : '获取图层信息失败')
  }
})

function getAllUids(data: Tree[]): string[] {
  let uids: string[] = []
  data.forEach((node) => {
    uids.push(node.uid)
    if (node.children && node.children.length > 0) {
      uids = uids.concat(getAllUids(node.children))
    }
  })
  return uids
}

async function checkChange(data: any, node: any) {
  const layers = map.listLayers()
  const ids = node.checkedKeys.filter((item: any) => item.includes('.'))
  if (data.label === '四川DEM') return toggleLayer('dem', 'demSource', map.getLayer('tdtCva') ? 'tdtCva' : 'dy')
  if (data.label === '天地图') {
    toggleLayer('tdtVec', 'tdtVec', map.getLayer('dem') ? 'dem' : map.getLayer('tdtCva') ? 'tdtCva' : 'dy')
    return toggleLayer('tdtCva', 'tdtCva', 'dy')
  }
  if (data.label === '矢量地图') return toggleLayer('tdtVec', 'tdtVec', map.getLayer('dem') ? 'dem' : map.getLayer('tdtCva') ? 'tdtCva' : 'dy')
  if (data.label === '矢量注记') return toggleLayer('tdtCva', 'tdtCva', 'dy')

  if (data.label === '四川旅游景点聚合图') {
    toggleClusterLayer('clusters', {
      id: 'clusters',
      type: 'circle',
      source: 'sichuan-jingdian',
      filter: ['has', 'point_count'],
      paint: {
        'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 750, '#f28cb1'],
        'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40]
      }
    })
    toggleClusterLayer('cluster-count', {
      id: 'cluster-count',
      type: 'symbol',
      source: 'sichuan-jingdian',
      filter: ['has', 'point_count'],
      layout: {
        'text-field': '{point_count_abbreviated}',
        'text-font': ['Open Sans Semibold'],
        'text-size': 12
      }
    })
    toggleClusterLayer('unclustered-point', {
      id: 'unclustered-point',
      type: 'circle',
      source: 'sichuan-jingdian',
      filter: ['!', ['has', 'point_count']],
      paint: {
        'circle-color': '#11b4da',
        'circle-radius': 4,
        'circle-stroke-width': 1,
        'circle-stroke-color': '#fff'
      }
    })
    if (node.checkedKeys.includes('4')) {
      map.on('click', 'clusters', clickClustersMap)
      map.on('click', 'unclustered-point', clickUnclusteredPoint)
      map.on('mouseenter', 'clusters', mouseenterMap)
      map.on('mouseenter', 'unclustered-point', mouseenterMap)
      map.on('mouseleave', 'clusters', mouseleaveMap)
      map.on('mouseleave', 'unclustered-point', mouseleaveMap)
    } else {
      map.off('click', 'clusters', clickClustersMap)
      map.off('click', 'unclustered-point', clickUnclusteredPoint)
      map.off('mouseenter', 'clusters', mouseenterMap)
      map.off('mouseenter', 'unclustered-point', mouseenterMap)
      map.off('mouseleave', 'clusters', mouseleaveMap)
      map.off('mouseleave', 'unclustered-point', mouseleaveMap)
    }
    return
  }

  if (data.label === '航向图') {
    if (node.checkedKeys.includes('5')) {
      const { dotPoint, flyLine, flyLine2 } = await useL7Layer()
      map.addLayer(dotPoint)
      map.addLayer(flyLine)
      map.addLayer(flyLine2)
      const element = document.querySelector('.l7-control-logo')! as HTMLElement
      element.style.display = 'none'
    } else {
      layers.forEach((item: any) => {
        if (item.startsWith('l7_layer')) map.removeLayer(item)
      })
    }
    return
  }

  if (data.label === '德阳市地图') {
    if (node.checkedKeys.includes('0')) {
      if (map.getLayer('dy')) {
        map.removeLayer('dy')
        map.removeSource('dyMapSource')
      }
      map.addSource('dyMapSource', {
        type: 'raster',
        tileSize: 256,
        tiles: [URL_CONFIGS.DY_MAP_URL],
        rasterSource: 'iserver'
      })
      map.addLayer({
        id: 'dy',
        type: 'raster',
        source: 'dyMapSource',
        minzoom: 0,
        maxzoom: 22
      })
    } else {
      if (map.getLayer('dy')) {
        map.removeLayer('dy')
        map.removeSource('dyMapSource')
      }
    }
    return
  }

  if (data.label === '成都矢量瓦片地图') {
    if (node.checkedKeys.includes('3')) {
      if (map.getSource('chengdu')) map.removeSource('chengdu')
      map.addStyle(URL_CONFIGS.CHENGDU_MAP_URL)
      if (map.getLayer('highlight-feature-layer')) {
        map.removeLayer('highlight-feature-layer')
        map.removeSource('highlight-feature-source')
      }
      setTimeout(() => {
        // queueMicrotask(() => {
        map.addSource('highlight-feature-source', {
          type: 'geojson',
          data: {
            type: 'FeatureCollection',
            features: []
          }
        })
        map.addLayer({
          id: 'highlight-feature-layer',
          type: 'fill',
          source: 'highlight-feature-source',
          paint: { 'fill-color': '#FF0000', 'fill-opacity': 0.8 }
        })
        // })
      }, 1500)
    } else {
      if (map.getLayer('highlight-feature-layer')) {
        map.removeLayer('highlight-feature-layer')
        map.removeSource('highlight-feature-source')
      }
      layers.forEach((item: any) => {
        if (item.startsWith('chengdu@SiChuan')) map.removeLayer(item)
      })
      if (map.getSource('chengdu')) map.removeSource('chengdu')
    }
    return
  }

  if (data.label === '管网地图') {
    if (node.checkedKeys.includes('6')) {
      map.addSource('pipe-map', {
        type: 'raster',
        tileSize: 256,
        tiles: [URL_CONFIGS.PIPE_MAP_URL],
        rasterSource: 'iserver'
      })
      map.addLayer({
        id: 'pipe-map',
        type: 'raster',
        source: 'pipe-map',
        minzoom: 0,
        maxzoom: 22
      })
      await addFacilityPoint()
    } else {
      if (map.getLayer('pipe-map')) {
        map.removeLayer('pipe-map')
        map.removeSource('pipe-map')
      }
      removeFacilityPoint()
    }
    return
  }

  if (map.getLayer('dy')) map.removeLayer('dy')
  if (map.getSource('dyMapSource')) map.removeSource('dyMapSource')
  map.addSource('dyMapSource', {
    type: 'raster',
    tileSize: 256,
    tiles: [`${URL_CONFIGS.DY_MAP_URL}?layersID=[${ids}]`],
    rasterSource: 'iserver'
  })
  map.addLayer({
    id: 'dy',
    type: 'raster',
    source: 'dyMapSource',
    minzoom: 0,
    maxzoom: 22
  })
}

function toggleClusterLayer(layerId: string, layerConfig: MapboxLayerOptions) {
  if (map.getLayer(layerId)) {
    map.removeLayer(layerId)
  } else {
    map.addLayer(layerConfig)
  }
}

function toggleLayer(layerId: string, sourceId: string, beforeLayerId: string) {
  if (map.getLayer(layerId)) {
    map.removeLayer(layerId)
  } else {
    map.addLayer(
      {
        id: layerId,
        type: 'raster',
        source: sourceId,
        minzoom: 0,
        maxzoom: 22
      },
      map.getLayer(beforeLayerId) ? beforeLayerId : null
    )
  }
}

const handleDoubleClick = useDebounceFn((data, node) => {
  if (node.checked) map.fitBounds(LAYER_BOUNDS[data.label as BoundsKey] ?? LAYER_BOUNDS['default'], { padding: 200 })
}, 500)

function handleNodeClick(data: any, node: any) {
  const currentTime = new Date().getTime()
  if (currentTime - lastClickTime < 300 && lastClickedNode === node) handleDoubleClick(data, node)
  lastClickTime = currentTime
  lastClickedNode = node
}

async function addFacilityPoint() {
  try {
    const queryParam = {
      getFeatureMode: 'SQL',
      datasetNames: ['pipe:pipe_Node'],
      maxFeatures: 1000,
      queryParameter: { attributeFilter: 'SmNodeID in ' + `(${SOURCENODE_IDS.join(',')})` }
    }
    const facilityPointGeojson = await http.post<GeoJSON.FeatureCollection<GeoJSON.Point>>(URL_CONFIGS.PIPE_DATA_URL, queryParam)
    map.addSource('valve-points', {
      type: 'geojson',
      data: facilityPointGeojson
    })
    map.addLayer({
      id: 'valve-points',
      type: 'circle',
      source: 'valve-points',
      paint: { 'circle-radius': 6, 'circle-color': '#003153', 'circle-opacity': 1 }
    })

    map.addLayer({
      id: 'valve-label',
      type: 'symbol',
      source: {
        type: 'geojson',
        data: facilityPointGeojson
      },
      layout: {
        'text-field': '阀门',
        'text-offset': [0, -1.5],
        'text-anchor': 'top',
        'text-size': 16
      },
      paint: { 'text-color': '#003153', 'text-opacity': 1 }
    })
  } catch (error) {
    throw new Error('获取阀门数据失败')
  }
}

function removeFacilityPoint() {
  if (map.getLayer('valve-points')) {
    map.removeLayer('valve-points')
    map.removeSource('valve-points')
  }

  if (map.getLayer('valve-label')) {
    map.removeLayer('valve-label')
    map.removeSource('valve-label')
  }
}

function clickClustersMap(e) {
  const features = map.queryRenderedFeatures(e.point, { layers: ['clusters'] })
  const clusterId = features[0].properties.cluster_id
  map.getSource('earthquakes').getClusterExpansionZoom(clusterId, function (err, zoom) {
    if (err) return
    map.easeTo({
      center: features[0].geometry.coordinates,
      zoom: zoom
    })
  })
}

function clickUnclusteredPoint(e) {
  const features = map.queryRenderedFeatures(e.point, { layers: ['unclustered-point'] })
  if (features.length > 0) {
    new mapboxgl.Popup().setText(features[0].properties.名称).setLngLat([e.lngLat.lng, e.lngLat.lat]).addTo(map)
  }
}

const mouseenterMap = () => (map.getCanvas().style.cursor = 'pointer')
const mouseleaveMap = () => (map.getCanvas().style.cursor = '')
</script>

<template>
  <div class="layer">
    <div class="layer-container">
      <el-scrollbar height="100%">
        <el-tree
          ref="treeRef"
          :data="layersData"
          show-checkbox
          node-key="uid"
          :props="defaultProps"
          :default-expanded-keys="['0', '1']"
          @check="checkChange"
          @node-click="handleNodeClick"
          class="custom-tree" />
      </el-scrollbar>
    </div>
  </div>
</template>

<style scoped lang="scss">
.layer {
  position: absolute;
  top: 55px;
  height: 47vh;
  width: 250px;
  left: 8px;
  z-index: 9;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  .layer-container {
    height: 47vh;
    .custom-tree {
      border-radius: 6px;
      background-color: #0a1831a0;
      color: #ccc;
    }
    :deep(.el-tree-node__content:hover) {
      background-color: #0f4d99;
    }

    :deep(.el-tree-node.is-current > .el-tree-node__content) {
      background-color: #1f4b5c8e;
    }

    :deep(.el-tree-node:focus > .el-tree-node__content) {
      background-color: #1f4b5c8e;
    }
  }
}
</style>
